package com.jinjie.exception;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.jinjie.base.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;

@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(ServletException.class)
    public Result<Object> handleServletException(ServletException ex, HttpServletRequest request) {
        // 处理ServletException
        return Result.error("500", "服务端异常，请联系管理员");
    }

    /**
     * 缺少请求参数异常
     *
     * @param ex HttpMessageNotReadableException
     * @return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public Result<Object> handleHttpMessageNotReadableException(MissingServletRequestParameterException ex) {
        log.error("缺少请求参数，{}", ex.getMessage());
        return Result.error("400", "缺少必要的请求参数");
    }

    /**
     * 空指针异常
     *
     * @param ex NullPointerException
     * @return
     */
    @ExceptionHandler(NullPointerException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public Result<Object> handleTypeMismatchException(NullPointerException ex) {
        log.error("空指针异常，{}", ex.getMessage());
        return Result.error("500", ex.getMessage());
    }

    /**
     * Http消息不可读异常。
     * <p>
     * 报错原因包括（不完全的列举）：
     * <p>
     * （1）缺少请求体（RequestBody）异常;
     * <p>
     * （2）无效格式异常。比如：参数为数字，但是前端传递的是字符串且无法解析成数字。
     * <p>
     * （3）Json解析异常（非法Json格式）。传递的数据不是合法的Json格式。比如：key-value对中的value(值)为String类型，却没有用双引号括起来。
     * <p>
     * 举例：
     * （1）缺少请求体（RequestBody）异常。报错：
     * DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotReadableException:
     * Required request body is missing:
     * public void com.example.web.user.controller.UserController.addUser(com.example.web.model.param.UserAddParam)]
     */
    @ExceptionHandler
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public Result<String> handle(HttpMessageNotReadableException e, HandlerMethod handlerMethod) {
        Throwable rootCause = e.getRootCause();

        // 无效格式异常处理。比如：目标格式为数值，输入为非数字的字符串（"80.5%"、"8.5.1"、"张三"）。
        if (rootCause instanceof InvalidFormatException) {
            String format = "HttpMessageNotReadableException-InvalidFormatException（Http消息不可读异常-无效格式异常）：%s";
            String errorMessage = String.format(format, e.getMessage());
            return Result.error(String.valueOf(HttpStatus.BAD_REQUEST.value()), errorMessage);
        }

        String userMessage = "Http消息不可读异常！请稍后重试，或联系业务人员处理。";
        String errorMessage = String.format("HttpMessageNotReadableException（Http消息不可读异常）：%s", userMessage);
        return Result.error(String.valueOf(HttpStatus.BAD_REQUEST.value()), errorMessage);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public Result<?> handlerNoFoundException(Exception e) {
        log.error(e.getMessage(), e);
        return Result.error(404, "路径不存在，请检查路径是否正确");
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public Result<?> handleDuplicateKeyException(DuplicateKeyException e) {
        log.error(e.getMessage(), e);
        return Result.error("数据库中已存在该记录");
    }

    /**
     * 自定义业务异常
     *
     * @param ex NullPointerException
     * @return
     */
    @ExceptionHandler(CommonRuntimeException.class)
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public <T> Result<T> handleCommonException(CommonRuntimeException ex) {
        log.error("自定义业务异常，{}", ex.getMessage());
        return Result.error(ex.getCode(), ex.getMessage());
    }

    /**
     * 捕获运行时异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    public Result<Object> handleRuntimeException(RuntimeException e) {
        log.error("handleRuntimeException:", e);
        return Result.error(e.getMessage());
    }
}
